<?php
/**
 * 百度大脑OCR类
 */
namespace soa\ocr\baiduBrain;

use artisan\cache;
use artisan\db;
use artisan\http;
use soa\ACPreg\ACPreg;
use soa\check\EnvCheck;
use soa\ocr\baiduOcr\thermalPaperOcr;
use soa\ocr\baiduOcr\baiduOcr;
use soa\ocr\baiduOcr\generalOcr;

/**
 * 百度大脑识别类
 * Class baiduBrainOCR
 * @package soa\ocr\baiduBrain
 */
class baiduBrainOCR{
    //配置参数
    protected $appId     = '';
    protected $appKey    = '';
    protected $secretKey = '';
    protected $account = [
        '0' => [//zzx百度账号
            'appId'    => '9249695',
            'appKey'   => 'q97rHB2aMGmGiDsrfnLnemhO',
            'secretKey'=> 'WERhAisanbKHEvG0pMYvkiWhpfMUYZxy',
        ],
        '1' => [//快宝公司账号
            'appId'    => '9568854',
            'appKey'   => 'OWzX2eyxpbrdV5woHMAHDqEX',
            'secretKey'=> 'PmHNWoGFxK89d7VjgtRemN9fwVaGjQCV',
        ],
        '2' => [//wzy
            'appId'    => '9568874',
            'appKey'   => '6OalhOeiHzCj4gxVuIuGLeSG',
            'secretKey'=> 'yAWMVxxwTVDxmzEyYWD8tQGCGZWYTcGB',
        ],
        '3' => [//zyz
            'appId'    => '9568891',
            'appKey'   => 'TXU7YSzT7GECfsedhpFmrSsD',
            'secretKey'=> 'qaDInWGFS8T6gpUfDjNendxVCGGRAw9',
        ],
        '4' => [//ysp
            'appId'    => '9569266',
            'appKey'   => 'eOIKluqL68cqcjQtNgBvMZnu',
            'secretKey'=> 'HCZqQ5f4yqTezwIkmwrmlmkBlfEU2FW0',
        ],
        '5' => [//zyz
            'appId'    => '9569291',
            'appKey'   => 'CE0tOIPHnR4yIRgCEaoZ3Oom',
            'secretKey'=> 'p8aEbsnCN5qiOWPiezxZGz2rz3tYtuGM',
        ],
//        '6' => [//mc
//            'appId'    => '9569291',
//            'appKey'   => 'CE0tOIPHnR4yIRgCEaoZ3Oom',
//            'secretKey'=> 'p8aEbsnCN5qiOWPiezxZGz2rz3tYtuGM',
//        ],
//        '7' => [//mc
//            'appId'    => '9569291',
//            'appKey'   => 'CE0tOIPHnR4yIRgCEaoZ3Oom',
//            'secretKey'=> 'p8aEbsnCN5qiOWPiezxZGz2rz3tYtuGM',
//        ],
    ];


    /**
     * 调用百度身份证识别接口，返回识别结果
     * @param $imgPath  图片地址
     * @param bool $isFront   默认true表示识别正面  ；   false表示识别反面
     * @param array $options  其他参数， 默认['detect_direction'=>true]表示支持图片旋转
     * @return array 返回识别效果
     */
    public function baiduIdCardOCR($imgPath, $isFront = true, $options = ['detect_direction'=>'true']){
//        $aipOcr = new AipOcr($this->appId, $this->appKey, $this->secretKey);
//        idCardOCR($imgPath, $isFront = true, $options = ['detect_direction'=>'true'])
            return generalOcr::idCardOCR($imgPath, $isFront, $options);
/*
        if(EnvCheck::isEnvOnline()){//线上正式环境
            $aipOcr = new AipOcr('9568854', 'OWzX2eyxpbrdV5woHMAHDqEX', 'PmHNWoGFxK89d7VjgtRemN9fwVaGjQCV');
//            logDebug('OnlineEnv', 'isEnvOnlineIdCardOCRDebug');
        }else{//81和online_test环境
//            logDebug('testEnv', 'isEnvOnlineIdCardOCRDebug');
            $aipOcr = new AipOcr('9569291', 'CE0tOIPHnR4yIRgCEaoZ3Oom', 'p8aEbsnCN5qiOWPiezxZGz2rz3tYtuGM');
        }

        //身份证识别
        $ocrStartTime = time();
        $ret = $aipOcr->idcard(file_get_contents($imgPath), $isFront, $options);
//        return $ret;
        $return = [];
        if(isset($ret['words_result']) && is_array($ret['words_result'])){//成功有返回结果的
            if($isFront){//识别正面
                $return['name'] = $ret['words_result']['姓名']['words']? : '';
                $return['birthday'] = $ret['words_result']['出生']['words']? : '';
                $return['sex'] = $ret['words_result']['性别']['words']? : '';
                $return['address'] = $ret['words_result']['住址']['words']? : '';
                $return['id'] = $ret['words_result']['公民身份号码']['words']? : '';
                $return['nation'] = $ret['words_result']['民族']['words']? : '';
            }else{//识别反面
                $return['startDate'] = $ret['words_result']['签发日期']['words']? : '';
                $return['endDate'] = $ret['words_result']['失效日期']['words']? : '';
                $return['policeStation'] = $ret['words_result']['签发机关']['words']? : '';
            }
        }elseif(isset($ret['error_code']) && isset($ret['error_msg'])){//报错的

        }

        $isNeedPay = false;//是否需要计费（支付给百度的费用，对账用）
        if(
        !empty($return) ||
        (isset($ret['error_code']) && $ret['error_code'] == 216633) ||
        (isset($ret['error_code']) && $ret['error_code'] == 216631)
        ){
            $isNeedPay = true;
        }

        $insertData = [
            'timeout'       => time() - $ocrStartTime,//调用接口耗时
            'ocr_time'      => date('Y-m-d H:i:s'),//识别时间
            'pic_path'      => $imgPath,//图片路径
            'original_data' => json_encode($ret, JSON_UNESCAPED_UNICODE),//接口原始返回结果json格式
            'result'        => json_encode($return, JSON_UNESCAPED_UNICODE),//处理后的返回结果json格式
            'supplier'      => 'baidu',//调用的百度的接口
            'status'        => !empty($return) ? 'success' : 'fail',//是否成功
            'is_need_pay'   => $isNeedPay ? 1 : 0,//百度那边是否需要扣费
            'is_front'      => $isFront ? 1 : 0,//是否正面 1-正面；0-反面
        ];
        $thermalPaperDBObj = db::connect('users')->table('tbl_idcard_ocr');

        $ret = $thermalPaperDBObj->insert($insertData);
        if(!$ret){
            logDebug(json_encode([
                'timeOut'    => time() - $ocrStartTime,//调用接口耗时
                'return'     => $ret,//原始返回值
                'insertData' => $insertData,//待入库参数
            ], JSON_UNESCAPED_UNICODE), 'kdhelp_IdCardOcr_insertErr', true);
        }
        return $return;
*/
    }


    /**
     * 面单识别
     * @param $imgPath
     * @param string $brand
     * @return array
     */
    public function thermalPaperOCR($imgPath, $brand='sto', $cmPhone=''){
        if(empty($imgPath))
            raise(1009, '没有需要识别的面单！');

        $ocrRet = thermalPaperOcr::thermalPaperOcr($imgPath, $brand, $ocrWhat='all', $cmPhone);
        $return = [
            'waybillNo'     => (isset($ocrRet['waybillNo']) && $ocrRet['waybillNo']) ? $ocrRet['waybillNo'] : '',
            'sendName'      => (isset($ocrRet['senderName']) && $ocrRet['senderName']) ? $ocrRet['senderName'] : '',
            'sendPhone'     => (isset($ocrRet['senderPhone']) && $ocrRet['senderPhone']) ? $ocrRet['senderPhone'] : '',
            'sendAddress'   => (isset($ocrRet['senderAddress']) && $ocrRet['senderAddress']) ? $ocrRet['senderAddress'] : '',
            'receiveName'   => (isset($ocrRet['receiveName']) && $ocrRet['receiveName']) ? $ocrRet['receiveName'] : '',
            'receivePhone'  => (isset($ocrRet['receivePhone']) && $ocrRet['receivePhone']) ? $ocrRet['receivePhone'] : '',
            'receiveAddress'=> (isset($ocrRet['receiveAddress']) && $ocrRet['receiveAddress']) ? $ocrRet['receiveAddress'] : '',
        ];
        return $return;
    }



    public function thermalPaperOCRBak($imgPath, $brand = 'sto'){
        /*
        //目前百度每个账号每天可以识别500次
        for($i=0,$max=(count($this->account)-1);$i<=$max;$i++){
            $cacheKey = 'baiduBrainOCR_'.$i;
            $cache = cache::get($cacheKey);
            if(empty($cache)){
                $cache = 0;
                $timestamp = strtotime(date('Y-m-d').' 23:59:59');
                cache::set($cacheKey, 0, 86400);
                cache::expireat($cacheKey, $timestamp);
            }
            if($cache < 495){//保留5次
                $this->appId     = $this->account[$i]['appId'];
                $this->appKey    = $this->account[$i]['appKey'];
                $this->secretKey = $this->account[$i]['secretKey'];
                cache::incr($cacheKey);
                break;
            }elseif($i == $max){
                raise(9999, '超过当天识别上限！');
                break;
            }
        }
        */
//echo $this->appId.'<br>'.$this->appKey.'<br>'.$this->secretKey;die;

//        header('content-type:text/html;charset=utf-8');
        if(empty($imgPath))
            raise(1009, '没有需要识别的面单！');

        //调用百度通用文字识别接口
        $originalData = $this->baiduBrainOCR($imgPath);
//logDebug($originalData, 'baiduocrerror');
//以下为百度识别返回的参数示例：返回是数组格式，此处便于书写，处理为json格式
/*
        $t = '
{"log_id":85989209,"classify_result":{"lottery":"unknown"},"words_result":[{"location":{"left":303,"top":255,"width":441,"height":169},"words":"\u2220O0"},{"location":{"left":712,"top":366,"width":409,"height":51},"words":" TO EXPRESS"},{"location":{"left":1488,"top":275,"width":1130,"height":165},"words":"\u4ee3\u6536\u8d27\u6b3e:\uffe50.00"},{"location":{"left":2547,"top":539,"width":252,"height":169},"words":"\u6d3e"},{"location":{"left":602,"top":984,"width":1582,"height":228},"words":"358074125874"},{"location":{"left":1232,"top":1267,"width":511,"height":267},"words":"\u592a\u539f"},{"location":{"left":756,"top":1634,"width":307,"height":173},"words":"\u592a\u539f"},{"location":{"left":1827,"top":1689,"width":724,"height":114},"words":"2016-0912"},{"location":{"left":618,"top":1945,"width":2047,"height":133},"words":"\u6536\u4ef6\u4eba:u\u675c\u4e09\u624b\u673a\/\u7535\u8bdd:15809632569"},{"location":{"left":610,"top":2059,"width":2004,"height":169},"words":"\u5730\u5740:\u5c71\u897f\u7701\u592a\u539f\u5e02\u5c0f\u5e97\u533a\u5f00\u53ef\u662f\u53ef\u662f\u53ef\u662f"},{"location":{"left":378,"top":2165,"width":1602,"height":165},"words":"\u7f73\u53ef\u662f\u51ef\u6492\u51ef\u6492\u54b3\u55fd\u54b3\u55fd\u55d6\u55d6\u55d6"},{"location":{"left":594,"top":2425,"width":539,"height":106},"words":"\u5bc4\u4ef6\u4eba:\u90fd\u6709"},{"location":{"left":1476,"top":2429,"width":996,"height":137},"words":"\u624b\u673a\/\u7535\u8bdd:15812345678"},{"location":{"left":590,"top":2523,"width":1834,"height":149},"words":"\u5730\u5740:\u4e0a\u6d77\u5e02\u4e0a\u6d77\u5e02\u957f\u5b81\u533a\u65b0\u6cfe\u9547\u5317\u7fdf\u8def1196\u53f7"},{"location":{"left":594,"top":2622,"width":523,"height":106},"words":"\u5efa\u6ed4\u5546\u4e1a\u5e7f\u573a"},{"location":{"left":614,"top":2827,"width":649,"height":106},"words":"\u5185\u5bb9\u54c1\u540d:\u9526\u7ee3\u9999\u6c5f"},{"location":{"left":614,"top":2957,"width":307,"height":102},"words":"\u8ba1\u8d39\u91cd\u91cf"},{"location":{"left":1567,"top":2850,"width":834,"height":192},"words":"\u7b7e\u6536\u4eba\/\u7b7e\u6536\u65f6\u95f4"},{"location":{"left":606,"top":3079,"width":441,"height":102},"words":"\u58f0\u660e\u4ef7\u503c:\uffe5"},{"location":{"left":1571,"top":3055,"width":1157,"height":181},"words":"\u60a8\u7684\u7b7e\u5b57\u4ee3\u8868\u60a8\u5df2\u9a8c\u6536\u6b64\u5305\u5609,\u5e76\u5df2\u786e\u8ba4"},{"location":{"left":602,"top":3205,"width":578,"height":94},"words":"\u4ee3\u6536\u8d27\u6b3e:\uffe50.D0"},{"location":{"left":1575,"top":3130,"width":1137,"height":181},"words":"\u5546\u54c1\u4fe1\u606f\u65e0\u8bef,\u5305\u88c5\u5b8c\u597d,\u6ca1\u6709\u5212\u75d5\u3001\u7834"},{"location":{"left":1634,"top":3220,"width":480,"height":106},"words":"\u7b49\u8868\u9762\u8d28\u91cf\u95ee\u9898\u3002"},{"location":{"left":590,"top":3327,"width":456,"height":110},"words":"\u5230\u4ed8\u91d1\u989d:\uffe5"},{"location":{"left":2161,"top":3264,"width":484,"height":216},"words":"\u6708\u65e5"},{"location":{"left":185,"top":3610,"width":2244,"height":417},"words":"\u76df\u8f7b\u677e\u5bc4\u4ef6,\u7801\u4e0a\u638c\u63e1"},{"location":{"left":2642,"top":3713,"width":165,"height":149},"words":"\u6536"}],"words_result_num":27}
';
        $originalData = json_decode($t, true);
*/
        $return = [];
        if(isset($originalData['words_result']) && !empty($originalData['words_result']) && is_array($originalData['words_result'])){//百度有返回识别结果

            $allData = [];

            //整理百度返回的数据，写入到$allData
            foreach($originalData['words_result'] as $k=>$v){
                $allData[$k] = [
                    'lineNo'=> $k,
                    'left'  => $v['location']['left'],//左边距
                    'top'   => $v['location']['top'],//上边距
                    'width' => $v['location']['width'],//宽
                    'height'=> $v['location']['height'],//高
                    'words' => $v['words'],//内容
                ];
            }
//            unset($originalData);

            //获取面单上的运单号
//            $getWaybillNoRet = $this->getWaybillNo($allData, $brand);
//            $return['waybillNo'] = empty($getWaybillNoRet) ? '' : $getWaybillNoRet;
            $wordsArr = array_column($originalData['words_result'], 'words');
            $waybillNoRet = thermalPaperOcr::getWaybill($wordsArr, $brand);
            $return['waybillNo'] = $waybillNoRet['waybillNo'];//运单号




            //获取面单关键点定位坐标等。。。
            $location = $this->location($allData);
//var_dump($location);

            //根据面单定位对收发件人信息进行鉴别分类
            $identifyRet = $this->identify($location, $allData);
            $sendData    = [];//发件人数据
            foreach($identifyRet['sendData'] as $k=>$v){
                $sendData[] = [
                    'lineNo' => $v['lineNo'],
                    'top'    => $allData[$v['lineNo']]['top'],
                    'left'   => $allData[$v['lineNo']]['left'],
                    'width'  => $allData[$v['lineNo']]['width'],
                    'height' => $allData[$v['lineNo']]['height'],
                    'words'  => $v['words'],
                    'wordsNum'=> strlen($v['words']),
                    'x'      => $v['vector']['x'],
                    'y'      => $v['vector']['y'],
                    'l'      => $v['vector']['l'],
                    'area'   => $v['area'],
                    'quotient' => $v['quotient'],
                ];
            }
            $receiveData = [];//收件人数据
            foreach($identifyRet['receiveData'] as $k=>$v){
                $receiveData[] = [
                    'lineNo' => $v['lineNo'],
                    'top'    => $allData[$v['lineNo']]['top'],
                    'left'   => $allData[$v['lineNo']]['left'],
                    'width'  => $allData[$v['lineNo']]['width'],
                    'height' => $allData[$v['lineNo']]['height'],
                    'words'  => $v['words'],
                    'wordsNum'=> strlen($v['words']),
                    'x'      => $v['vector']['x'],
                    'y'      => $v['vector']['y'],
                    'l'      => $v['vector']['l'],
                    'area'   => $v['area'],
                    'quotient' => $v['quotient'],
                ];
            }
            unset($identifyRet);

            //获取寄件人电话号码
            $sendPhone = $this->getPhone($sendData);
            $return['sendPhone'] = empty($sendPhone) ? '' : $sendPhone;
            //获取收件人电话号码
            $receivePhone = $this->getPhone($receiveData);
            $return['receivePhone'] = empty($receivePhone) ? '' : $receivePhone;

            //获取寄件人地址
            $sendAddressRet = $this->getAddress($sendData);
            $return['sendAddress'] = empty($sendAddressRet) ? '' : $sendAddressRet;

            //获取收件人地址
            $receiveAddressRet = $this->getAddress($receiveData);
            $return['receiveAddress'] = empty($receiveAddressRet) ? '' : $receiveAddressRet;

            //获取发件人姓名
            $getNameRet = $this->getName(['0'=>$location['sendLocationInfo']], $type='send');//寄件人姓名
            if(empty($getNameRet))//如果没解析出收件人姓名，从所有数据中再尝试一次
                $getNameRet = $this->getName($allData, $type='send');//寄件人姓名
            $return['sendName'] = empty($getNameRet) ? '' : $getNameRet;

            //获取收件人姓名
            $getNameRet = $this->getName(['0'=>$location['receiveLocationInfo']], $type='receive');//收件人姓名
            if(empty($getNameRet))//如果没解析出发件人姓名，从所有数据中再尝试一次
                $getNameRet = $this->getName($allData, $type='receive');//收件人姓名
            $return['receiveName'] = empty($getNameRet) ? '' : $getNameRet;
        }else{//百度无返回识别结果
            logDebug([
                'originalImgPath' => $imgPath,
                'brand' => $brand,
                'baiduOCRRet' => $originalData,
            ], 'baiduBrainOCRError');
            raise(300001, '识别失败！');
        }

///*
        $thermalPaperDBObj = db::connect('orders')->table('tbl_thermalpaper_ocr');
        $ret = $thermalPaperDBObj->insert([
            'ocr_time' => date('Y-m-d H:i:s'),
            'brand'    => $brand,
            'pic_path' => $imgPath,
            'pic_path_md5'  => md5($imgPath),
            'original_data' => json_encode($originalData),
            'result'        => json_encode($return),
        ]);
//*/

//        echo '<hr>';
//        var_dump($return);
//        echo '<hr>';
//        var_dump($allData);
        return $return;
    }


    /**
     * 调用百度通用文字识别接口，返回识别结果
     * @param $imgPath
     * @return array
     */
    public function baiduBrainOCR($imgPath){
//        $aipOcr = new AipOcr($this->appId, $this->appKey, $this->secretKey);
        //通用文字识别
//        $ret = $aipOcr->general(file_get_contents($imgPath),[
//            'language_type'    => 'CHN_ENG',
//            'detect_direction' => true,
//            'vertexes_location'=> true,
//        ]);
        $ret = baiduOcr::generalWithLocation($imgPath);//百度通用文字识别（含位置信息版）
        return $ret;
    }

    /**
     * 找出权重最高的收，发相关信息位置坐标&信息
     * @param $allData
     * @return array
     */
    protected function location($allData){
        //$rule $sendRuleNum $receiveRuleNum三个参数需要同步一起修改
        $rule = '/(?<sendName1>寄件人.*)|(?<sendName2>发件人.*)|(?<sendName3>寄件.*)|(?<sendName4>发件.*)|(?<sendName5>寄.*)|(?<sendName6>发.*)|(?<receiveName1>收件人.*)|(?<receiveName2>收件.*)|(?<receiveName3>收.*)/';
//        $rule = '/(?<sendName1>寄件人.*)|(?<sendName2>发件人.*)/';
        $sendRuleNum    = 6;//sendName 1~？
        $receiveRuleNum = 3;//receiveName 1~？
        $max = 6;//取$sendRuleNum和$receiveRuleNum中的大值
        $return = $sendName  = $receiveName = [];
        foreach($allData as $k=>$v){
            $a = preg_match_all($rule, $v['words'], $matchs);
            for($i=1;$i<=$sendRuleNum;$i++){//匹配sendName 1~？
                if(isset($matchs['sendName'.$i]['0']) && !empty($matchs['sendName'.$i]['0'])){
                    $sendName[$i][] = [//$i为//命中的权重 1最大
//                        'level' => $i,//命中的权重
                        'lineNo'=> $v['lineNo'],//编号
                        'left'  => $v['left'],//左边距
                        'top'   => $v['top'],//上边距
                        'width' => $v['width'],//宽
                        'height'=> $v['height'],//高
                        'words' => $v['words'],//内容
                    ];
                    break;
                }
            }
            for($j=1;$j<=$receiveRuleNum;$j++){//匹配receiveName 1~？
                if(isset($matchs['receiveName'.$j]['0']) && !empty($matchs['receiveName'.$j]['0'])){
                    $receiveName[$j][] = [//$j为命中的权重 1最大
//                        'level' => $j,//命中的权重
                        'lineNo'=> $v['lineNo'],//编号
                        'left'  => $v['left'],//左边距
                        'top'   => $v['top'],//上边距
                        'width' => $v['width'],//宽
                        'height'=> $v['height'],//高
                        'words' => $v['words'],//内容
                    ];
                    break;
                }
            }
        }
        for($k=1;$k<=$max;$k++){
            if(isset($sendName[$k]['0']) && !empty($sendName[$k]['0'])){
                $return['sendLocationInfo'] = empty($return['sendLocationInfo']) ? $sendName[$k]['0'] : $return['sendLocationInfo'];
            }
            if(isset($receiveName[$k]['0']) && !empty($receiveName[$k]['0'])){
                $return['receiveLocationInfo'] = empty($return['receiveLocationInfo']) ? $receiveName[$k]['0'] : $return['receiveLocationInfo'];
            }
        }

        //（寄 - 收） 信息左右之差
        $dCol = $return['sendLocationInfo']['left'] - $return['receiveLocationInfo']['left'];
        //（寄 - 收） 信息上下之差
        $dRow = $return['sendLocationInfo']['top'] - $return['receiveLocationInfo']['top'];
        if(abs($dCol)>abs($dRow)){//确定面单为左右格式
            $return['type'] = 'leftRight';
            $return['dCol'] = $dCol;//列像素差（正数表示收件人在左，寄件人在右）
        }else{//确定面单为上下格式
            $return['type'] = 'upDown';
            $return['dRow'] = $dRow;//行像素差（正数表示收件人在上，寄件人在下）
        }
        return $return;
    }

    /**
     * 遍历信息找出运单号
     * @param $data
     * @return mixed
     */
    protected function getWaybillNo($data, $brand){
        $ruleNum = 1;
        $rule = '/(?<waybillNo1>[1-9]\d{11,12})/';
        if($brand == 'sto'){
            $ruleNum = 6;
            $rule = '/(?<waybillNo1>[1-9]\d{11,12})|(?<waybillNo2>(588|688|888|900|((11|22)\d{1})|((33|44|55|77|99)\d{2})|((66|88)([0-7]|9)\d{1}))\d{9})|(?<waybillNo3>4[012356789][0-9]{10})|(?<waybillNo4>22[^79]\w{9})|(?<waybillNo5>118\w{9,10})|(?<waybillNo6>(33|55|77)\w{11})/';
        }//申通面单规则
        foreach($data as $k=>$info){
            $a = preg_match_all($rule, $info['words'], $matchs);
            for($i=1;$i<=$ruleNum;$i++){
                if(isset($matchs['waybillNo'.$i]['0']) && !empty($matchs['waybillNo'.$i]['0'])){
                    return $matchs['waybillNo'.$i]['0'];
                }
            }
        }
    }

    /**
     * 遍历信息找出收（寄）件人姓名
     * @param $data
     * @param string $type     s表示寄件人；r表示收件人
     * @return mixed
     */
    protected function getName($data, $type='send'){
        if($type == 'send'){//寄件人规则
            $ruleNum = 10;
            $rule = '/(寄件人姓名:(?<name1>.*))|(寄件人姓名(?<name2>.*))|(寄件人:(?<name3>.*))|(寄件人(?<name4>.*))|(寄(?<name5>.*))|(发件人姓名:(?<name6>.*))|(发件人姓名(?<name7>.*))|(发件人:(?<name8>.*))|(发件人(?<name9>.*))|(发(?<name10>.*))/';
        }elseif($type == 'receive'){//收件人规则
            $ruleNum = 5;
            $rule = '/(收件人姓名:(?<name1>.*))|(收件人姓名(?<name2>.*))|(收件人:(?<name3>.*))|(收件人(?<name4>.*))|(收(?<name5>.*))/';
        }else{
            return '';
        }
        foreach($data as $v){
            preg_match_all($rule, $v['words'], $matchs);

            for($i=1;$i<=$ruleNum;$i++){
                if(isset($matchs['name'.$i]['0']) && !empty($matchs['name'.$i]['0'])){
                    return $matchs['name'.$i]['0'];
                }
            }
        }
    }

    /**
     * 遍历信息找出电话号码
     * @param $data
     * @return string
     */
    protected function getPhone($data){
        $phone = '';
        $phoneArr = [];
        $ruleNum = 5;
        $rule = '/(?<phone1>1\d{10})|(?<phone2>0\d{10,11})|(?<phone3>0\d{2,3}(.*?)\d{7,8})|(?<phone4>\d{8})|(?<phone5>\d{7})/';
        foreach($data as $k=>$info){
            preg_match_all($rule, $info['words'], $matchs);
            for($i=1;$i<=$ruleNum;$i++){
                if(isset($matchs['phone'.$i]['0']) && !empty($matchs['phone'.$i]['0'])){
                    $phoneArr[$i] = $matchs['phone'.$i]['0'];
                }
            }
        }
        for($j=1;$j<=$ruleNum;$j++){
            if(isset($phoneArr[$j]) && !empty($phoneArr[$j])){
                $phone = empty($phone) ? $phoneArr[$j] : $phone;
            }
        }
        return $phone;
    }

    /**
     * 根据面单定位对收发件人信息进行鉴别分类
     * @param $location
     * @param $allData
     * @return array
     */
    protected function identify($location, $allData){
        $return = $sendData = $receiveData = [];
        if(($location['type'] == 'upDown') && ($location['dRow'] >= 0)){//上下格式，收件人在上，寄件人在下
            foreach($allData as $k=>$v){
                if(($v['top'] >= ($location['sendLocationInfo']['top'] - $location['sendLocationInfo']['height'])) && ($v['top'] <= ($location['sendLocationInfo']['top'] + $location['dRow']))){//（此处必须 >= ）寄件人在下，以寄件人top值为分割线，（暂不考虑误差）
                    //获取向量
                    $vector = $this->getVector([
                        'left'=> $location['sendLocationInfo']['left'],
                        'top' => $location['sendLocationInfo']['top'],
                    ], [
                        'left'=> $v['left'],
                        'top' => $v['top'],
                    ]);
                    $area = abs($vector['x'] * $vector['y']);
                    $sendData[] = [
                        'lineNo' => $v['lineNo'],//行号
                        'words'  => $v['words'],
                        'vector' => $vector,//$v点 以 寄件人为原点（向右为x正轴，向下为y正轴）的向量值
                        'area'   => $area,//两点在笛卡尔坐标系平行于top分割先构成的正方形面积
                        'quotient'=> $vector['l'] * $area,//距离系数
                    ];
                }elseif(($v['top'] >= ($location['receiveLocationInfo']['top'] - $location['receiveLocationInfo']['height']) && ($v['top'] <= $location['receiveLocationInfo']['top'] + $location['dRow']))){//（此处必须 >= ）否则则为靠近收件人信息
                    $vector = $this->getVector([
                        'left'=> $location['receiveLocationInfo']['left'],
                        'top' => $location['receiveLocationInfo']['top'],
                    ], [
                        'left'=> $v['left'],
                        'top' => $v['top'],
                    ]);
                    $area = abs($vector['x'] * $vector['y']);
                    $receiveData[] = [
                        'lineNo' => $v['lineNo'],//行号
                        'words'  => $v['words'],
                        'vector' => $vector,//$v点 以 收件人为原点（向右为x正轴，向下为y正轴）的向量值
                        'area'   => $area,//两点在笛卡尔坐标系平行于top分割先构成的正方形面积
                        'quotient'=> $vector['l'] * $area,//距离系数
                    ];
                }
            }

            $return = [
                'sendData'    => $this->generalSort($sendData, 'quotient', 8),
                'receiveData' => $this->generalSort($receiveData, 'quotient', 8),
            ];
        }elseif(($location['type'] == 'upDown') && ($location['dRow'] < 0)){//上下格式，收件人在下，寄件人在上
            foreach($allData as $k=>$v){
                if(($v['top'] >= ($location['receiveLocationInfo']['top'] - $location['receiveLocationInfo']['height'])) && ($v['top'] <= ($location['receiveLocationInfo']['top'] - $location['dRow']))){//（此处必须 >= ）收件人在下，以收件人top值为分割线，（暂不考虑误差）
                    //获取向量
                    $vector = $this->getVector([
                        'left'=> $location['receiveLocationInfo']['left'],
                        'top' => $location['receiveLocationInfo']['top'],
                    ], [
                        'left'=> $v['left'],
                        'top' => $v['top'],
                    ]);
                    $area = abs($vector['x'] * $vector['y']);
                    $receiveData[] = [
                        'lineNo' => $v['lineNo'],//行号
                        'words'  => $v['words'],
                        'vector' => $vector,//$v点 以 收件人为原点（向右为x正轴，向下为y正轴）的向量值
                        'area'   => $area,//两点在笛卡尔坐标系平行于top分割先构成的正方形面积
                        'quotient'=> $vector['l'] * $area,//距离系数
                    ];
                }elseif(($v['top'] >= ($location['sendLocationInfo']['top'] - $location['sendLocationInfo']['height'])) && ($v['top'] <= $location['sendLocationInfo']['top'] - $location['dRow'])){
                    $vector = $this->getVector([
                        'left'=> $location['sendLocationInfo']['left'],
                        'top' => $location['sendLocationInfo']['top'],
                    ], [
                        'left'=> $v['left'],
                        'top' => $v['top'],
                    ]);
                    $area = abs($vector['x'] * $vector['y']);
                    $sendData[] = [
                        'lineNo' => $v['lineNo'],//行号
                        'words'  => $v['words'],
                        'vector' => $vector,//$v点 以 收件人为原点（向右为x正轴，向下为y正轴）的向量值
                        'area'   => $area,//两点在笛卡尔坐标系平行于top分割先构成的正方形面积
                        'quotient'=> $vector['l'] * $area,//距离系数
                    ];
                }
            }
            $return = [
                'sendData'    => $this->generalSort($sendData, 'quotient', 8),
                'receiveData' => $this->generalSort($receiveData, 'quotient', 8),
            ];
        }elseif(($location['type'] == 'leftRight') && ($location['dCol'] >= 0)){//左右格式，收件人在左，寄件人在右

        }elseif(($location['type'] == 'leftRight') && ($location['dCol'] < 0)){//左右格式，收件人在右，寄件人在左

        }else{
            $return = [
                'sendData'    => [],
                'receiveData' => [],
            ];
        }
        return $return;
    }

    /**
     * 获取地址
     * @param $data
     * @return array
     */
    protected function getAddress($data){
        $address = '';//最终返回的地址字符串
        $ACObj = new ACPreg();

//        $temp = [];
        foreach($data as $v){
            $keyWords = $ACObj->findWordsInArray($this->addressKeyWords(), $v['words']);
            $countKeyWords = count($keyWords);
            //含有 省，市，区 三个关键词的直接当做是地址字符串
            $isAddress = (count(array_merge($keyWords, ['省', '市', '区'])) < ($countKeyWords + 3)) ? true : false;
            if($countKeyWords >= 3 || $isAddress){
                $address = $address.$v['words'];
            }
        }
        return $address;

 /*
        //旧版匹配地址
        $address = '';
        $maxLen = [];
        //遍历找出地址第一行
        foreach($data as $k=>$v){
            if(($v['l'] > 0) && (empty($maxLen) || ($v['wordsNum'] > $maxLen['wordsNum']))){//去除收，寄两条信息
                $maxLen  = $v;
                $address = $v['words'];
            }
        }

        //遍历尝试找出地址第二行
        $uTemp = $address2 = '';
        foreach($data as $k=>$v){
            if($v['lineNo'] > $maxLen['lineNo']){//第二行必须在第一行下面，所以行号比第一行大
                $lineTop = $v['top'] - $maxLen['top'];//两行之间像素高度差
                $lineHeight = ($v['height'] + $maxLen['height'])/2;//行高像素平均值
                $u = 100 * ($lineTop - $lineHeight) / $lineHeight;
                $address2 = ($u < $uTemp) ? $v['words'] : $address2;
            }
        }
        $address = $address.$address2;
        return $address;
*/
    }

    /**
     * 计算两点间的向量差
     * @param $zero
     * @param $point
     * @return array
     */
    protected function getVector($zero, $point){
        return [
            'x' => $point['left'] - $zero['left'],
            'y' => $point['top'] - $zero['top'],
            'l' => (int)(pow(($point['left'] - $zero['left']), 2) + pow(($point['top'] - $zero['top']), 2)),//sqrt
        ];
    }

    /**
     * 按某一字段排序二维数组，从小到大，截取N个
     * @param $data
     * @param $key
     * @param $num
     * @return array
     */
    protected function generalSort($data, $key, $num){
        $return = $t1 = [];
        foreach($data as $v){
            $t1[] = $v[$key];
        }
        array_multisort($t1,SORT_ASC,$data);
        for($i=0;$i<$num;$i++){
            if(isset($data[$i]) && !empty($data[$i])){
                $return[] = $data[$i];
            }else{
                break;
            }
        }
        return $return;
    }

    /**
     * 地址中常见的900个词
     * @return array
     */
    protected function addressKeyWords(){
        return ['街','路','道','号','省','镇','区','大','东','南','城','市','中','西','山','园','北','新','江','河','学','近','靠','靠近','广','华','小','业','国','建','海','福','金','场','楼','家','院','东省','大道','工','花','州','阳','安','村','小区','南省','商','公','行','苑','湖','宁','一','龙','店','二','民','东路','桥','发','福建','银','兴','三','文','县','开','广东','河南','大街','四','花园','大学','广东省','乡','门','南路','北路','学院','明','天','广场','心','人','通','五','银行','开发','校','厦','长','开发区','发区','关','西路','中心','山东','滨','中国','林','湾','水','岗','苏','光','河南省','化','云','农','社','沙','和','山东省','南街','科','口','高','江苏','里','平','江苏省','栋','川','支','环','州市','庄','元','大厦','中路','山路','巷','乐','支行','北省','城街','美','校区','工业','永','万','社区','白','德','人民','地','司','技','凤','经','市场','期','松','城关','府','站','江省','号楼','际','子','务','公司','国际','景','医','电','武','陵','都','河北','理','富','济','馆','关镇','城关镇','青','溪','生','儿','族','星','政','岗街','酒','宝','浙','港','马','泉','局','东街','浙江','松岗','自','西省','黄','红','爱','华路','服','丰','师','河北省','源','京','岸','山街','上','分','西街','田','塘','百','室','浙江省','治','道中','酒店','石','合','城区','香','家园','术','方','峰','四川','名','盛','达','兰','所','物','六','幼','自治','康','利','医院','第','八','道东','泰','范','步','川省','四川省','品','台','古','辽','幼儿','幼儿园','云南','十','风','车','汇','山镇','育','远','昌','新村','营','太','代','居','世','王','桥街','侨','昆','集','徽','前','安徽','道南','师范','会','辽宁','五爱','洋','自治区','七','后','岭','有','嘉','河街','双','贸','座','辽宁省','园街','浦','单','力','保','头','庭','云南省','博','限','春','经济','雅','吉','阳光','莲','蕉','技术','设','文化','有限','教','重','丽','部','下','信','江路','有限公司','庆','卫','农业','洲','新城','单元','宾','滨河','城镇','安徽省','商业','装','运','山西','湖北','一路','民路','正','南大','江镇','产','隆','城市','祥','交','绿','清','科技','连','农业银行','朝','东侨','银行','北街','洪','黑','学校','服务','时','快','沈','购','区金','货','宁德','建设','九','月','惠','湖南','城南','桂','雨','管','高新','虹','园路','面','街镇','沈阳','锦','鑫','段','行街','食','杨','城东','谷','原','彩','同','岛','玉','柳','西乡','团','购物','河路','朝阳','厂','放','昆明','尚','邮','州路','塔','胜','宅','燕','道金','庙','湖南省','厚','解','解放','小南','步行','淳','淳化','镇中','步行街','队','中山','唐','汽','中华','寓','新华','工商','洛','联','递','莱','处','尔','瑞','百货','广西','吴','公寓','纪','亭','东方','大酒店','人民路','快递','业园','机','临','之','昌镇','紫','口街','产业','内','南区','环路','金沙','壮族','贵','日','安镇','广西壮族自治区','顺','管理','北大','明街','群','湖北省','世纪','长江','房','职','来','申','道街','沟','李','小学','江西','道文','镇新','井','珠','和平','立','罗','南京','豪','二路','乡街','郑','泽','客','公园','经济开发区','重庆','中学','张','鼎','成','道长','蕉城','路东','闽','汽车','老','化路','角','夏','佳','屯','省道','师范大学','范大','职业','药','科学','崇','广州','温','润','宁德市','东海','现','申通','财','军','堂','群力','委','进','东城','商务','恒','铺','琴','鹤','良','道华','州街','北陵','汉','南通','荆','食品','厚街','桥镇','服务中心','东风','区中','阳街','义','兴路','津','中州','外','黎','山西省','门市','程','仁','蓝','迎','文化路','华街','长安','庄镇','宏','海南','新区','茶','服装','碧','江西省','山庄','伊','溪镇','中华路','龙江','广州市','郑州','厦门','裕','万达','圣','贵州','三路','双福','现代','御','办','英','南大街','南岸','村街','正街','阁','教育','公馆','园区','黑龙江','淮','黑龙','对','华苑','基','林街','区新','堡','灵','凯','秀','大市','附','大市场','白云','西区','体','季','中街','贵州省','北京','工街','州省','理工','新街','后宅','客运','凤城','学府','奥','道北','书','河东','道学','工程','工业区','业区','福街','厦门市','仙','兴街','四季','洛阳','陕','车站','航','对面','坪','道海','麦','阜','沙洲','海岸','门街','黑龙江省','区东','出','一号','昆明市','凤城镇','总','道松','艺','新技术','新技','高新技术','贝','工业园','时代','服装城','莞','鲁','梁','环城','商城','莲花','古城','城路','亚','客运站','号院','悦','东莞','路南','雨花','肥','吉林','山大','云区','白云区','海南省','狮','海街','北区','特','北大街','河镇','蕉城区','哈','山水','农场','浴','幢','创','派','光彩','陕西','武路','年','旅','陵镇','黎明','村镇','卫生','福州','意','梅','黄河','动','洪武','峰镇','营街','术学','湖街','回','格','荣','陕西省','厚街镇','海路','板','县城','友','储','城东街','南山','滨路','连城','寨','山区','坊','二楼','税','未','电子','验','江宁','宁路','阳路','深','鸿','丘','市东','凤凰','工农','铁','尼','铜','主','新南','龙路','益','唐山','蓄','侯','翔','储蓄','色','商丘','林省','木','吉林省','崇文','建昌','邮政','胜利','丹','钟','旗','呈','国工','城北','岗镇','线','中央','术学院','呈贡','虹桥','东区','道黄','商贸','横','迎宾','远洋','中国工商银行','闽东','文路','堤','象','回族','工大','师范学院','区西','南环','赵','河口','未来','涛','峪','荷','东莞市','莞市','霞','业路','堰','启','华东','合肥','一期','中国银行','顺街','宁夏','华南','牛','宁夏回族','宁夏回族自治区','镇西','层','周','体育','奎','演','芙','文鼎','镇金','流','田街','庙街','镇东','语','草','贤','江东','沧','厢','锋','工程学院','购物中心','沿','成都','松涛','桃','南镇','城厢','浦路','蓉','湖滨','图','永安','观','乡道','深圳','圳','花苑','牙','东岗','定','交通','海沧','集镇','店镇','沙河','宁南','四大','爱客','晋','西苑','路交','朗','才','兰花','东兴','南城','二号','米','民主','果','华庭','芙蓉','派出所','派出','邮电','奎镇','怡','寮'];
    }
}
